#include "geometry.h"

#include <cmath>

/**
 * Point
 */
Point::Point( int a, int b ) noexcept
    : x( a )
    , y( b ) {}

Point::~Point() {}

Point Point::operator+( const Point & p ) const {
    return Point( x + p.x, y + p.y );
}

Point Point::operator-( const Point & p ) const {
    return Point( x - p.x, y - p.y );
}

/**
 * Edge
 */
Edge::Edge( Point a, Point b ) noexcept
    : from( a )
    , to( b ) {}

Edge::~Edge() {}

Edge::Position Edge::relPos( const Point & p ) const {
    Point a = to - from;
    Point b = p - from;
    int64_t area = a.x * b.y - b.x * a.y;
    if ( area < 0 )
    {
        return RIGHT;
    } else if ( area > 0 )
    {
        return LEFT;
    }
    /**
     * точка на той же линии что и отрезок
     * если точка до отрезка - вектора направленые в противоположные стороны
     * если точка впереди отрезка - вектор b строго больше вектора a
     * иначе точка принадлежит отрезку
     */
    else if ( a.x * b.x < 0 || a.y * b.y < 0 )
    {
        return BACK;
    } else if ( abs( b.x ) > abs( a.x ) || abs( b.y ) > abs( a.y ) )
    {
        return FRONT;
    } else
    { return BELONG; }
}

bool Edge::isCrossing( const Edge & e ) const {
    /**
     * Либо точки лежат слева и справа относительно отрезка(обратное тоже необходимо)
     * Либо одна из точке принадлежит отрезку.
     */
    auto posFrom = relPos( e.from ), posTo = relPos( e.to );
    auto posEFrom = e.relPos( from ), posETo = e.relPos( to );
    return ( posFrom == BELONG || posTo == BELONG || posEFrom == BELONG || posETo == BELONG )
           || ( ( ( posFrom == LEFT && posTo == RIGHT ) || ( posFrom == RIGHT && posTo == LEFT ) )
                && ( ( posEFrom == LEFT && posETo == RIGHT ) || ( posEFrom == RIGHT && posETo == LEFT ) ) );
}

double Edge::polarAngle( const Point & from, const Point & to ) {
    Point v = to - from;
    double cosx = v.x / std::sqrt( v.x * v.x + v.y * v.y );
    return v.y >= 0 ? std::acos( cosx ) * 180 / M_PI : 360 - std::acos( cosx ) * 180 / M_PI;
}

double Edge::polarAngle( const Edge & e ) {
    return polarAngle( e.from, e.to );
}

/**
 * Vertex
 */
Vertex::Vertex( int a, int b ) noexcept
    : Point( a, b )
    , Node() {}

Vertex::Vertex( const Point & p ) noexcept
    : Point( p.x, p.y )
    , Node() {}

Vertex::~Vertex() {}

Vertex * Vertex::cw() {
    return static_cast<Vertex *>( next );
}

Vertex * Vertex::cww() {
    return static_cast<Vertex *>( prev );
}

/**
 * Figure
 */
Figure::Figure( std::unique_ptr<Vertex> v )
    : vertexes( std::move( v ) ) {
    resize();
}

Figure::~Figure() {}

bool Figure::isPointIn( const Point & p ) const {
    if ( vertexes )
    {
        Vertex * v = vertexes.get();
        for ( size_t i = 0; i < size; ++i )
        {
            Edge::Position pos = Edge( *v, *v->cw() ).relPos( p );
            if ( pos != Edge::BELONG && pos != Edge::RIGHT )
            { return false; }
            v = v->cw();
        }
        return true;
    }
    return false;
}

bool Figure::isCrossing( const Edge & e ) const {
    if ( vertexes )
    {
        Vertex * v = vertexes.get();
        for ( size_t i = 0; i < size; ++i )
        {
            if ( Edge( *v, *v->cw() ).isCrossing( e ) )
            { return true; }
            v = v->cw();
        }
    }
    return false;
}

void Figure::resize() {
    if ( vertexes )
    {
        size = 1;
        Vertex * nv = vertexes->cw();
        while ( nv != vertexes.get() )
        {
            size++;
            nv = nv->cw();
        }
    } else
        size = 0;
}
